from collections import OrderedDict
import datetime
import numpy as np
import math

def convert_dict_to_date_router_list(data):
    jaccard_input = {}
    for key in data.keys():
        for single_date in data[key]:
            if jaccard_input.has_key(single_date[0]):
                jaccard_input[single_date[0]] = jaccard_input[single_date[0]] + [key]
            else:
                jaccard_input[single_date[0]] = [key]
                
    for key in jaccard_input:
        jaccard_input[key] = list(set(jaccard_input[key]))
        
    return OrderedDict(sorted(jaccard_input.items()))
    
def to_vector_data(data, start_date, end_date):
    vector_data = []
    for router in data:
        s_vector = []
        s_date = start_date
        e_date = end_date
        while s_date <= e_date:
            if s_date in [item[0] for item in data[router]]:
                s_vector.append(1)
            else:
                s_vector.append(0)
            s_date = s_date + datetime.timedelta(minutes = 1)
        vector_data.append(s_vector)
    return np.array(vector_data) 

def to_vector_data_removal(data, start_date, end_date):
    router_names = []
    vector_data = []
    for router in data:
        s_vector = []
        s_date = start_date
        e_date = end_date
        while s_date <= e_date:
            if s_date in [item[0] for item in data[router]]:
                s_vector.append(1)
            else:
                s_vector.append(0)
            s_date = s_date + datetime.timedelta(minutes = 1)
        if s_vector.count(1) > 0:
            vector_data.append(s_vector)
            router_names.append(router)
    return [np.array(vector_data), router_names]
    
def LMdistance(v1, v2):
    return sum(v1 * v2) / float(min([sum(v1), sum(v2)]))
    
def LMsimilarity(v1, v2):
    #the same - 0
    #different 1
    v1 = np.array(v1)
    v2 = np.array(v2)    
    return abs(sum(v1 * v2) / float(min([sum(v1), sum(v2)])) - 1)
    
def close_to_location(router_vector, location_vectors, threshold):
    for loc_vect in location_vectors:
#        print LMsimilarity(router_vector, loc_vect)
        if LMsimilarity(router_vector, loc_vect) < threshold:
            return True
    return False
    
def get_location_ground_truth(class_data, start_date, end_date):
    location_gt = []
    stop_location_time = 10
    flag = True

    for idx in xrange(0, len(class_data.keys()) - 1):
        class_date = class_data.keys()[idx]
        next_class_date = class_data.keys()[idx + 1]
        if class_date >= start_date and class_date <= end_date:
            if "stand" in class_data[class_date]:
                flag = False
            if flag:
                location_gt.append(class_date)
                flag = False
            else:
                if ("stand" in class_data[class_date] or "start_data" in class_data[class_date]) and ((next_class_date - class_date).seconds / 60) > stop_location_time:
                    flag = True
                    location_gt.append(class_date)
    return location_gt
    
def drange(start, stop, step):
    r = start
    while r < stop:
        yield r
        r += step

#http://www.johndcook.com/python_longitude_latitude.html
def distance_on_unit_sphere((lat1, long1), (lat2, long2)):

    # Convert latitude and longitude to 
    # spherical coordinates in radians.
    degrees_to_radians = math.pi/180.0
        
    # phi = 90 - latitude
    phi1 = (90.0 - lat1)*degrees_to_radians
    phi2 = (90.0 - lat2)*degrees_to_radians
        
    # theta = longitude
    theta1 = long1*degrees_to_radians
    theta2 = long2*degrees_to_radians
        
    # Compute spherical distance from spherical coordinates.
        
    # For two locations in spherical coordinates 
    # (1, theta, phi) and (1, theta, phi)
    # cosine( arc length ) = 
    #    sin phi sin phi' cos(theta-theta') + cos phi cos phi'
    # distance = rho * arc length
    
    cos = (math.sin(phi1)*math.sin(phi2)*math.cos(theta1 - theta2) + 
           math.cos(phi1)*math.cos(phi2))
    arc = math.acos( cos )

    # Remember to multiply arc by the radius of the earth 
    # in your favorite set of units to get length.
    return arc * 6373

def calc_max_sequence(vector):
    max_count = 0
    curr_count = 0
    for num in vector:
        if num == 1:
            curr_count = curr_count + 1
#            print curr_count
        else:
            if max_count < curr_count:
                max_count = curr_count
            curr_count = 0
    return max_count
    
def get_location_coordinates(gps_data, location_dates, start_date, end_date):
    all_dates = [start_date] + location_dates + [end_date]
    gps_coord = OrderedDict()
    for idx in xrange(0, len(all_dates), 2):
        for gps_date in gps_data.keys():
            if gps_date >= all_dates[idx] and gps_date <= all_dates[idx + 1]:
                gps_coord[(all_dates[idx], all_dates[idx + 1])] = gps_data[gps_date]
                break
    return gps_coord
    
def router_life(transportation_routers_prospect, start_date, end_date, start_day, end_day):
    new_router_slope = OrderedDict()
    
    trans_routers = OrderedDict()
    router_life = []
    for router in transportation_routers_prospect.keys():
        min_date = min([item[0] for item in transportation_routers_prospect[router]]) - datetime.timedelta(minutes = 15)
        max_date = max([item[0] for item in transportation_routers_prospect[router]]) + datetime.timedelta(minutes = 15)
        if min_date >= start_date and end_date <= max_date:
            trans_routers[router] = transportation_routers_prospect[router]
#            router_life.append(len(transportation_routers_prospect[router]))
#            router_life.append(((max_date - min_date).seconds / 60))
#            print transportation_routers_prospect[router]
#            print len(transportation_routers_prospect[router]), ((max_date - min_date).seconds / 60)
            
    vect = to_vector_data(trans_routers, start_day, end_day)
    for v in vect:
        seq = calc_max_sequence(v)
        router_life.append(seq)
    
    vect = to_vector_data(transportation_routers_prospect, start_date, end_date)
    for v in vect:
        appearing_date = get_appearing_date(start_date, v)
        if appearing_date != None:
            if new_router_slope.has_key(appearing_date):
                new_router_slope[appearing_date] = new_router_slope[appearing_date] + 1
            else:
                new_router_slope[appearing_date] = 1
    
    time = (end_date - start_date).seconds / float(60)
    
    if len(trans_routers) > 0:
        return [np.median(router_life), max(router_life), time / float(len(trans_routers)), new_router_slope]
    else:
        return [0, 0, 0, {}]
        
def get_appearing_date(date, v):
    prev_val = 0
    for idx in xrange(len(v)):
        curr_val = v[idx]
        if prev_val == 0 and curr_val == 1:
            print v
            return date + datetime.timedelta(minutes = idx)
        prev_val = curr_val
    return None

def new_router_slope_window(router_slope, window_size, start_date, end_date):
    windowed_router_slope = OrderedDict()
    curr_date = start_date + datetime.timedelta(minutes = window_size)
        
    flag = True
    while curr_date <= end_date:
        count = 0
        for slope_date in router_slope.keys():
            if slope_date > curr_date - datetime.timedelta(minutes = window_size) and slope_date <= curr_date:
                count = count + router_slope[slope_date]
        if flag == True:
            for i in xrange(window_size, 0, -1):
                windowed_router_slope[curr_date - datetime.timedelta(minutes = i)] = count
            flag = False
        windowed_router_slope[curr_date] = count
        curr_date = curr_date + datetime.timedelta(minutes = 1)
        
    return windowed_router_slope
        
def calculate_speed(distance, time):
    # you have time in minutes, we want km/h
    
    # distance in KM
    # time has to be transformed to H
    time = time / float(60)
    return distance / time
    
def get_transportation_speed(gps_data, trans_start_time, trans_end_time):
    trans_gps_data = OrderedDict()
    for item in gps_data.keys():
        if item <= trans_end_time and item > trans_start_time:
            trans_gps_data[item] = gps_data[item]
            
    curr_date = trans_start_time
    speed_data = OrderedDict()
    speed = 0
    while curr_date <= trans_end_time:
        if trans_gps_data.has_key(curr_date) and trans_gps_data.keys().index(curr_date) < len(trans_gps_data) - 1:
            #recalc speed
            next_date = trans_gps_data.keys()[trans_gps_data.keys().index(curr_date) + 1]
            curr_lat_long = trans_gps_data[curr_date]
            next_lat_long = trans_gps_data[next_date]
            dist = distance_on_unit_sphere(curr_lat_long, next_lat_long)
            time = (next_date - curr_date).seconds / float(60)
            speed = calculate_speed(dist, time)
        speed_data[curr_date] = speed
        curr_date = curr_date + datetime.timedelta(minutes = 1)
    return speed_data
    
def date_diff_in_minutes(date1, date2):
    if date1 > date2:
        return (date1 - date2).seconds / float(60)
    return -(date2 - date1).seconds / float(60)